home *** CD-ROM | disk | FTP | other *** search
/ Amoszine 8 / Amoszine 8 (Disk 1 of 3).adf / ARTS / AS_Steve_Prob.asc / AS_Steve_Prob.asc
Encoding:
Text File  |  1998-04-26  |  22.0 KB  |  447 lines

  1.       @2{USING LINKED LISTS IN AMOS
  2.       @3
  3.       }By Andy (I'm Gonna Solve It) Smith
  4.  
  5.       @5
  6.       Solving Steve 'Lamer' Bye's directory reading problem
  7.       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  8.       @1
  9.       Originally,  I  was  going  to  write articles on Linked Lists, and
  10.       using the system libraries.    However, when I saw Steve Bye's plea
  11.       for  help with a directory reader I thought,'Hey!    I can write an
  12.       article  on that and include the articles I was going to write.' So
  13.       that's what this article is about.
  14.       @3
  15.       Right, down to business.
  16.       @6
  17.       Linked Lists
  18.       ~~~~~~~~~~~~ @4
  19.       Steve's  problem  is  quite  a  difficult problem to solve at first
  20.       glance  due  to it's complexity and size.    However, all things in
  21.       programming  may  appear difficult at first but when the problem is
  22.       broken down into managable chunks, it suddenly becomes easy.    The
  23.       first  hurdle  to  jump  over  is  how  to  store the filenames and
  24.       directory  names.
  25. @4      Most  people  will  opt for arrays, because they know how they work
  26.       and they are easy to implement.   But stop to think for one moment.
  27.  
  28. @5
  29.       Do you know many directories and files there are?    I can tell you
  30.       now  that  you  don't,  without  a  doubt.     So  the problem this
  31.       presents  is  how  many  elements  to allocate in the array?    10?
  32.       50?     5,000,000?     You  could  define  an  array  to  hold  500
  33.       elements.     But  what  happens  if  there are more files?    Your
  34.       program  crashes,  thats  what.     Even  if it is enough the other
  35.       problem is that lots of memory is being wasted, due to the elements
  36.       of  the  array  not  being  used.    From this, you just might have
  37.       guessed  that  arrays  are  not suitable data structures to use for
  38.       storing filenames, and you would be correct in your assumption.
  39.  
  40. @3
  41.       The  most  suitable  data  structure  to use is the Linked List for
  42.       reasons  which  will  become apparent in a while.    In fact Linked
  43.       Lists  have  been  covered before in Amoszine, Issue 6 to be exact,
  44.       written  by  none  other  than  Thomas Lancaster.    Unfortunately,
  45.       Thomas  missed  the point of Linked Lists, I hope my explanation of
  46.       them clarifies things.
  47.  
  48. @7
  49.       The differences between arrays and Linked Lists
  50.       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @4
  51.       The difference between arrays and linked lists is that the array is
  52.       a  'static  data structure'.    This means that once it is defined,
  53.       its  size is fixed to how many elements you specified.    It cannot
  54.       shrink or expand in size, so if you fill your array, then tough!
  55. @5
  56.       A Linked List however is a 'dynamic data structure' which means its
  57.       size  is  not predefined and it can expand and shrink when it takes
  58.       your fancy.
  59. @3
  60.       The  elements of an array are stored consecutively in memory as the
  61.       diagram below shows:
  62. @1
  63.                 _____ _____ _____ _____ _____ _____ _____ _____
  64.                |     |     |     |     |     |     |     |     |
  65.       Values   | 23  |  2  | 13  |  44 |  76 |  1  |  0  |  99 |
  66.                |_____|_____|_____|_____|_____|_____|_____|_____|
  67.       Memory
  68.       Location  1024  1028  1032  1036  1040  1044  1048  1052
  69. @4
  70.       You  may  have noticed that the memory locations are incremented in
  71.       steps  of 4.    Why is this?    Because when you define an array to
  72.       hold  numbers,  it  takes  four bytes. 
  73.       This allows truly massive numbers to be stored.
  74. @3
  75.       The diagram of a linked list is very different, here it is:
  76. @1
  77.        _____ _____       _____ _____       _____ _____
  78.       |     |     |     |     |     |     |     |     |
  79.       | 23  |   ------->|  2  |   ------->| 13  | NULL|
  80.       |_____|_____|     |_____|_____|     |_____|_____|
  81.       ^     ^           ^     ^           ^     ^
  82.       |     |           |     |           |     |
  83.       Value   |         Value   |         Value   |
  84.       |                 |                 |
  85.       Pointer           Pointer           Pointer
  86. @4
  87.       In  the diagram above, an element is the rectangle bit divided into
  88.       two  squares.     The  left  square is your actual data you want to
  89.       store, whether it is a number or a string it doesn't really matter.
  90. @5
  91.       The  square  on  the  right  of  the  element  is  whats known as a
  92.       'pointer'.     A  pointer  is  a variable like any other, except it
  93.       stores  addresses.    In AMOS there is no 'pointer type' so we just
  94.       use  an  ordinary  integer  variable.     This pointer contains the
  95.       address of the next element in the list.    It is important to note
  96.       however  that  the elements are NOT stored consecutively in memory,
  97.       like  arrays.     The  elements of a linked list can be ANYWHERE in
  98.       memory.     This  is  why a pointer is needed.    Notice the arrows
  99.       from  the  right  of each element pointing to the left hand side of
  100.       the  next  element.     This  shows you that each element points to
  101.       (contains the address of) the next element.    Also notice that the
  102.       last  element's  pointer contains NULL.    This is because we don't
  103.       want  it  to point anywhere as it is the last element.    Therefore
  104.       we  assign  the  pointer  the  value of NULL, which is zero in most
  105.       cases.
  106.       @3
  107.       IMPORTANT  NOTE:     In arrays, you can access an element directly.
  108.       So  if  you  wanted  to access the third element of an array called
  109.       HELLO,  you would use HELLO(2) (don't forget that arrays start from
  110.       0!!).     In  linked  lists  however,  you  can't.    If you want a
  111.       particular  element  from  the  list,  you  have  to start from the
  112.       beginning  of the list and go through it until you find the desired
  113.       item.
  114.       @4
  115.       One last thing to note about linked lists, each element can contain
  116.       2 pointers if you wish.    The address of the previous element, and
  117.       the address of the next element.    This is called a 2-way list, we
  118.       have utilised 1-way lists only.
  119.       @1
  120.  
  121.       Back to Thomas Lancaster's article on Linked lists, he says you can
  122.       use  arrays  to  create linked lists.    Wrong.    The fact that an
  123.       array's  size  is  fixed  means that you just can't use an array to
  124.       implement  a  linked  list,  it defeats the whole object of using a
  125.       linked  list  in the first place!    A linked list changes its size
  126.       remember!    In fact Thomas says that using an array to implement a
  127.       linked  list  "means  that  a  maximum  of 10 pieces of data can be
  128.       retained".     This  is  exactly  why  you cannot use arrays, it is
  129.       restricting.
  130.       @5
  131.       Further  on,  he  mentions  another  method  which  he says is more
  132.       efficient.     His idea is to use disk files to store the pointers.
  133.       How  can  it  be  more efficient if it is a file on a disk?    Disk
  134.       drives  are  many  more  times slower than memory.    Also, you can
  135.       only  open  a  maximum  of  10 files which means you are restricted
  136.       again.     I hope that clarifies things for Thomas and other people
  137.       who don't quite understand the theory of linked lists.
  138.       @3
  139.       Anyway, I had to get that off my chest, you wouldn't want people to
  140.       think that Amoszine spreads misinformation!
  141.       @7
  142.       Implementing linked lists in AMOS
  143.       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  144.       @4
  145.       Blitz  Basic  2 programmers think AMOS it not capable of very much.
  146.       Not  true,  AMOS  can  do  anything  any  other  language  can  do.
  147.       Perhaps  not  as  effectively,  but  it  can  be  done.     I can't
  148.       understand  why  no-one  uses  linked  lists  in AMOS because it is
  149.       really  simple.     It's  almost as simple as C.    The major thing
  150.       missing  from  AMOS  are  'structures'.    A structure in C is just
  151.       another  name  for a record.    In C, we need to utilise structures
  152.       to  be  able  to  use  linked  lists.    A suitable structure for a
  153.       linked list could be as follows:
  154.       @1
  155.       struct
  156.       {
  157.       char filename[80];
  158.       struct node *next;
  159.       } node;
  160.       @5
  161.       That  chunk  of code reserves space for a structure (record) called
  162.       node.     The declarations in the braces are the structures fields.
  163.       The  'char  filename[80]'  line  reserves an array of characters (a
  164.       string  basically)  called  filename  which can hold 80 characters.
  165.       @4
  166.       The  next declaration 'struct node *next' declares a pointer called
  167.       'next' which contains an address.    It is this address that points
  168.       to  the  next  element.     That  structure  is  in actual fact one
  169.       element  of  the  linked  list.     Refer  back to the diagram of a
  170.       linked list.
  171.       @3
  172.       As I said, structures are not implemented in AMOS, so this presents
  173.       a  problem.    When structures are defined all the elements in that
  174.       structure  are  stored one after the other in memory.    So we need
  175.       to  replicate  this in AMOS, which is quite easy.    All we need to
  176.       do  is  reserve  a wad of memory but you need to decide how much to
  177.       reserve.     This  is  quite  easy.    Look at the structure above.
  178.       @5
  179.       First  we  defined  a  string to hold 80 characters right?    So to
  180.       start  with,  we know we need to reserve as least 80 bytes.    Next
  181.       we need to store the pointer (the address of the next element).
  182.       @4
  183.       Addresses are stored as long words (4 bytes) so we need 4 bytes for
  184.       that.    So the total size of our structure is 84 bytes long.  
  185.       @3
  186.       Unfortunately,  AMOS  presents  us with yet another problem.    The
  187.       only  way  in  AMOS to reserve memory is to use a 'Reserve As.....'
  188.       instruction.    Example:
  189.       @1
  190.       Reserve as Work 15,84
  191.       @5
  192.       That command would reserve a memory bank 84 bytes long.    It would
  193.       be allocated using slot 15 of the memory banks.    Can you see what
  194.       the problem is yet??
  195.       @4
  196.       Well,  in  the original AMOS, you could only reserve banks 1 to 15,
  197.       so  we  could  only store 15 filenames!    In AMOS Pro however, the
  198.       memory  bank  system was vastly overhauled and you could reserve up
  199.       to  65535  banks.     But  we  are still constrained by what we can
  200.       reserve.     I  also  bet  that  there are more than 65535 files on
  201.       someones hard drive (including sub-directories of course).    Also,
  202.       if  we  did  use  memory banks, we would need to increment the slot
  203.       number (the first parameter in the Reserve As .....    instruction)
  204.       each  time  we wanted to reserve memory.    The solution then is to
  205.       go down to operating system level and use the functions in the Exec
  206.       library.     AAAArrrrrrrgggggggghhhhhhhhh,  I  hear you all scream!
  207.       Don't  worry,  it's  really easy.    The function we need to use is
  208.       called  AllocMem() and strangely enough it reserves some memory for
  209.       us.     I  won't  go into the mechanics of calling system libraries
  210.       here as this article will get too long.    If you want to learn how
  211.       to get your fingers dirty, then read my other article in this issue
  212.       on  how  to  do  such things!    All you need to know now though is
  213.       that we will be using this function.
  214.       @3
  215.       Developing the Algorithm
  216.       ~~~~~~~~~~~~~~~~~~~~~~~~@4
  217.       We  now know basically what we are doing.    However, it isn't time
  218.       to  jump  to your keyboards and start coding.    You need to design
  219.       it  on  paper  first.     This  is  VERY important.    When you are
  220.       developing  algorithms  you  are  not  quite  sure about you should
  221.       ALWAYS  design it first and dry run it to save you time and effort.
  222.       Many  a  time  I  have  jumped  in  head first and came out with no
  223.       results,  it  is  frustrating  I  can  assure you.    So here is my
  224.       pseudo-code for the linked list program:
  225.       @1
  226.       get the first filename;
  227.       allocate  some memory enough for a structure and remember the start
  228.       address;
  229.       REPEAT
  230.       store filename in structure;
  231.       allocate  some memory enough for a structure and remember the start
  232.       address;
  233.       get  start  address  and  store  in  pointer  element  of  previous
  234.       structure;
  235.       get next filename;
  236.       UNTIL NO MORE FILES;
  237.       store last filename in structure;
  238.       put NULL value into this elements pointer;
  239.       @4      
  240.       That  pseudo-code  will  read in a list of filenames and store each
  241.       one  in  its own structure.    Time to refine it more.    This time
  242.       our pseudo-code contains english and AMOS statements.
  243.       @1
  244.       ' Reserve 42 bytes of memory and store start address in
  245.       ' LIST
  246.       LIST=AllocMem(42);
  247.       
  248.       ' Remember the start address of our list, store in CURRENT
  249.       CURRENT=LIST;
  250.       
  251.       Get first filename;
  252.       REPEAT
  253.       
  254.       ' Store filename in memory area just reserved
  255.       populate_structure(CURRENT,filename);
  256.       
  257.       ' Reserve 42 bytes of memory and store start address in
  258.       ' NEWLOCATION
  259.       NEWLOCATION=AllocMem(42);
  260.       
  261.       ' Now this bit stores the address of our newly reserved
  262.       ' memory area in the memory area we reserved previously.
  263.       ' Loke is an AMOS command that stores a 4-byte number in
  264.       ' memory you specify.  This command has the effect of
  265.       ' storing the address of the next element in the current
  266.       ' element.
  267.       Loke CURRENT+38,NEWLOCATION;
  268.       
  269.       ' We now move onto the next element, which we just
  270.       ' reserved.  The CURRENT variable contains the address of
  271.       ' the element we are currently accessing.
  272.       CURRENT=NEWLOCATION;
  273.       
  274.       get next filename;
  275.       UNTIL NO MORE FILES;
  276.       
  277.       ' Store blank filename in last element
  278.       populate_structure(CURRENT,filename);
  279.       
  280.       ' Store NULL value in pointer field of structure.
  281.       Loke CURRENT+38,0
  282.       @5
  283.       You  may  be  wondering  why  I  have  reserved 42 bytes of memory.
  284.       This  is  because  the  Dir First$ and Dir Next$ functions return a
  285.       string  of 38 characters, plus we need a pointer as well, another 4
  286.       bytes, so that makes 42 bytes in total.
  287.       @4
  288.       The  pseudo-code above was very close to the AMOS equivalent so now
  289.       I  present  the  AMOS  version of the linked list program.    It is
  290.       also on the disk, it is called Linked_List.AMOS.
  291.       @1
  292.       ' Reserve 42 bytes of memory and store start address in
  293.       ' LIST
  294.       _ALLOC_MEM@1[@142,0]
  295.       LIST=Param
  296.       
  297.       ' Remember the start address of our list, store in CURRENT
  298.       CURRENT=LIST
  299.       
  300.       ' Get first filename
  301.       FILE$=Dir First$("**")
  302.       Repeat
  303.       ' Store filename in memory area just reserved
  304.       _POPULATE[CURRENT,FILE$]
  305.       
  306.       ' Reserve 42 bytes of memory and store start address in
  307.       ' NEWLOCATION
  308.       _ALLOC_MEM@1[@142,0]
  309.       NEWLOCATION=Param
  310.       
  311.       ' Now this bit stores the address of our newly reserved
  312.       ' memory area in the memory area we reserved previously.
  313.       ' Loke is an AMOS command that stores a 4-byte number in
  314.       ' memory you specify.  This command has the effect of
  315.       ' storing the address of the next element in the current
  316.       ' element.
  317.       Loke CURRENT+38,NEWLOCATION
  318.       
  319.       ' We now move onto the next element, which we just
  320.       ' reserved.  The CURRENT variable contains the address of
  321.       ' the element we are currently accessing.
  322.       CURRENT=NEWLOCATION
  323.       
  324.       ' Get next filename
  325.       FILE$=Dir Next$
  326.       
  327.       Until FILE$=""
  328.       _POPULATE[CURRENT,FILE$]
  329.       Loke CURRENT+38,0
  330.       
  331.       Procedure _POPULATE[CURRENT,LINE$]
  332.       Procedure _ALLOC_MEM[SIZE,REQUIREMENTS]
  333.       @4
  334.       If you compare this AMOS program and the pseudo-code that preceeded
  335.       it you will notice they are very similar.    Load AMOS now and load
  336.       the  file 'Linked_List.AMOS' into the editor.    Try it out and you
  337.       will  learn!     Notice  that in the source code that we have a few
  338.       more  procedures.    Namely _FREE_LIST and _FREE_MEM.    You should
  339.       call  the  _FREE_LIST  procedure  when  you have finished with your
  340.       list,  otherwise  it will take up valuable memory.    The _FREE_MEM
  341.       procedure  is  called  from  inside  the  _FREE_LIST  procedure and
  342.       basically it deletes one element from our list.
  343.       @5
  344.       Believe  it  or  not,  but we have jumped a major hurdle in Steve's
  345.       problem.     We  can  store  as  many  filenames as we like without
  346.       restrictions.     All  we  need  to  do  now  is  to get the entire
  347.       directory structure of a disk!
  348.       @3
  349.       Recursion recursion recursion recursion...........
  350.       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@4
  351.       Now  we  have  linked  lists  out  of the way, we can now deal with
  352.       scanning  all files of a disk.    The method we are going to use is
  353.       called  recursion.     Recursion  is where a procedure calls itself
  354.       based  on  some  condition.    The reason we are using recursion to
  355.       scan  directories  and subdirectories is because it is much simpler
  356.       to   code.      The  following  pseudo-  code  will  scan  a  disk
  357.       recursively:
  358.       @1
  359.       PROCEDURE _SCAN_DISK[filename]
  360.       scan directory for first file
  361.       repeat
  362.       if item scanned = directory THEN
  363.       _SCAN_DISK[item scanned]
  364.       ELSE
  365.       print filename
  366.       END IF
  367.       scan directory for next file
  368.       until NO MORE FILES
  369.       END PROC
  370.       @5
  371.       The  above  pseudo-code fragment loops until it can't find any more
  372.       files.     While it is scanning, if it comes across a file, then it
  373.       prints it out, with the full path attached.    However, if it finds
  374.       a  directory  then it calls itself i.e.    the _SCAN_DISK procedure
  375.       is  called  from  within itself.    This is what enables it to scan
  376.       directories.    When there are no more files in the directory, then
  377.       the  procedure  ends and control returns to the previous procedure.
  378.       As good as recursion may sound, it does have drawbacks.    Firstly,
  379.       if  recursive  procedures  are  not  controlled,  the  stack  could
  380.       overflow, resulting in your program crashing.    You can change the
  381.       size  of  the  stack  by  including a Set Stack instruction in your
  382.       programs.     It  requires  one  parameter, the number of recursive
  383.       calls  allowed.     It  is  unlikely  that anybody has more than 10
  384.       nested directories so there is no real problem.
  385.       @4
  386.       When  you  call  a  procedure,  or  subroutine,  the address of the
  387.       Program  Counter  is  pushed  onto the stack.    When the procedure
  388.       terminates,  the  address is popped off the stack.    However, when
  389.       using  recursion,  addresses keep getting pushed onto the stack and
  390.       if there isn't enough room, it overflows.
  391.       @3
  392.       There  is another drawback to using recursion, that is AMOS itself.
  393.       AMOS  isn't  too  friendly with recursion at all, it can crash, and
  394.       sometimes programs that use recursion don't compile properly.    It
  395.       makes  you  think sometimes if François Lionet can actually program
  396.       properly.     Included  on  this  disk (if Andy Gibson remembers to
  397.       include  my  source  code)  is  the  source  code for our directory
  398.       reading  program,  it  is called Dirstruc.AMOS.    Load it into the
  399.       editor  and run it.    When it prompts you for a directory to scan,
  400.       choose  DF0:.
  401.       @5
  402.       Don't  scan  your  hard  drive  as  it crashes, this is due to AMOS
  403.       again.     It  should  display  ALL your files on your floppy disk.
  404.       If  you have a look at the code, you will see I'm not using the Dir
  405.       First$  and  Dir  Next$  commands  to  scan  a  directory.     Why?
  406.       Because  they  are fraught with problems.    Firstly, it insists on
  407.       tagging  the  filesize  onto  the  returned string.    Secondly, it
  408.       doesn't  work  with  recursion.    So I am using functions from the
  409.       dos.library.     Don't worry yourself too much at this stage on how
  410.       it works, just accept that it does!    If you are REALLY interested
  411.       in how it all works, read my tutorial on calling libraries.
  412.       @4
  413.       Steve  wants  the directory structure to be sent to a file.    This
  414.       could  easily  be  done by using Print# statements instead of using
  415.       Print statements.    The trouble with this method is that the drive
  416.       heads  will  thrash.     By  this  I  mean  whilst  it is reading a
  417.       directory,  it  will  have to write to a file somewhere else, which
  418.       will  inevitably  slow  the  program  down a great deal.    This is
  419.       where  our  theory  on linked lists comes in.    Instead of writing
  420.       the  filenames  to  the  screen  or  a file, we shall store it in a
  421.       linked  list.     When  the entire disk has been scanned, we simply
  422.       scan  from  the start of the list to the end, reading each filename
  423.       from it and writing it into a file.    Load the file DirStruc2.AMOS
  424.       into  the  editor  and look at the source code.    Then try it out.
  425.       But  please  note, it is VERY VERY dodgy, and there's nothing I can
  426.       do  to  rectify it.
  427.       @3
  428.       AMOS has a lot to answer for sometimes.    I have also included a C
  429.       program  called DirStruc.C which does the same as the AMOS program,
  430.       but  more  efficiently and it doesn't crash.    If you have DICE it
  431.       will  compile  under  that.      If  you  don't,  then  there's  an
  432.       executable  version  that  you can use from the shell.    It may be
  433.       worth  noting that you could easily store the executable version in
  434.       a  memory bank and run it, solving our crashing problem.    It is a
  435.       quick  and  dirty  method  of  doing it, but AMOS leaves us with no
  436.       choice!     The  executable  version  is  called 'dirstruc' funnily
  437.       enough.    It needs to be run from the shell.
  438.       @4
  439.       Well  that's  it for this article, I hope you found it informative.
  440.       If  there  is  something you don't quite understand, contact me and
  441.       I'll  try  to  help  you.     If you want to see how to use library
  442.       functions, then  read my other article on this subject!
  443.  
  444.       Bye for now.
  445.       @1
  446.       end.
  447.